﻿using System;
using System.Collections.Concurrent;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using MiscUtil;
using TlvLib.SiChuan.Packet;

namespace TlvLib.SiChuan
{
    public class Facade
    {
        private readonly IPEndPoint _remoteEp;
        private SendHandler _sendHandler;
        private ReceiveHandler _rcvHandler;
        private ConcurrentDictionary<uint, CarInfo> _carInfosHistory
            = new ConcurrentDictionary<uint, CarInfo>();

        private BlockingCollection<CarInfo> _carInfosToBeSend
            = new BlockingCollection<CarInfo>();

        private Thread _thread;
        private CancellationTokenSource _cts;

        private NLog.Logger _logger = NLog.LogManager.GetCurrentClassLogger();

        public event EventHandler<EventArgs<Exception>> ErrorOccurred;
        public event EventHandler FirstRegisterSucceed;
        public event EventHandler SecondRegisterSucceed;

        public void OnSecondRegisterSucceed(EventArgs e)
        {
            EventHandler handler = SecondRegisterSucceed;
            if (handler != null) handler(this, e);
        }

        public event EventHandler ConnectionEstablished;
        public event EventHandler ConnectionClosed;

        public void OnConnectionClosed(EventArgs e)
        {
            EventHandler handler = ConnectionClosed;
            if (handler != null) handler(this, e);
        }


        public void OnRegisterSucceed(EventArgs e)
        {
            EventHandler handler = FirstRegisterSucceed;
            if (handler != null) handler(this, e);
        }


        public void OnConnectionEstablished(EventArgs e)
        {
            EventHandler handler = ConnectionEstablished;
            if (handler != null) handler(this, e);
        }

        public void OnErrorOccurred(EventArgs<Exception> e)
        {
            EventHandler<EventArgs<Exception>> handler = ErrorOccurred;
            if (handler != null) handler(this, e);
        }

        public Facade(IPEndPoint remoteEp)
        {
            if (remoteEp == null) throw new ArgumentNullException("remoteEp");
            _remoteEp = remoteEp;
        }



        private void ReportError(RegistrationResult reply)
        {
            Exception ex = null;
            if (reply.Error != null)
            {
                ex = reply.Error;
            }
            else if (!reply.Reply.Succeed)
            {
                var msg = string.Format("注册失败, 注册失败代码为{0}", reply.Reply.FailReason);
                ex = new Exception(msg);
            }

            if (ex != null)
            {
                OnErrorOccurred(new EventArgs<Exception>(ex));
            }

        }

        private void StartConnectThread(IPEndPoint reply)
        {
            _cts = new CancellationTokenSource();
            _thread = new Thread(ConnectThread);
            _thread.IsBackground = true;
            _thread.Start(reply);
        }

        private void ConnectThread(object remoteEp)
        {
            var ep = (IPEndPoint)remoteEp;
            var registered = false;
            TcpClient client = null;
            var wait = new EventWaitHandle(false, EventResetMode.AutoReset);
            while (true)
            {
                try
                {
                    if (_cts.IsCancellationRequested)
                    {
                        if (registered && client != null && client.Connected)
                        {
                            UnRegister(client);
                        }
                        break;
                    }

                    var register = new Register();
                    var regResult = register.RegistCore(ep, false);

                    if (RegistSucceed(regResult))
                    {
                        OnRegisterSucceed(EventArgs.Empty);

                        var secondEp = new IPEndPoint(IPAddress.Parse(regResult.Reply.Ip), regResult.Reply.Port);
                        var secondRegResult = register.RegistCore(secondEp, true);
                        if (RegistSucceed(secondRegResult))
                        {
                            OnSecondRegisterSucceed(EventArgs.Empty);
                            registered = true;
                            client = secondRegResult.Client;// new TcpClient();
                            //client.Connect(secondEp.Address, secondEp.Port);

                            _sendHandler = new SendHandler(_carInfosHistory, _carInfosToBeSend);
                            _sendHandler.ConnectionClosed += (sender, args) =>
                            {
                                OnConnectionClosed(EventArgs.Empty);
                                wait.Set();
                            };
                            _sendHandler.ErrorOccurred +=
                                (sender, args) => OnErrorOccurred(new EventArgs<Exception>(args.Value));

                            _rcvHandler = new ReceiveHandler(_carInfosHistory);
                            _rcvHandler.ConnectionClosed += (sender, args) =>
                            {
                                OnConnectionClosed(EventArgs.Empty);
                                wait.Set();
                            };
                            _rcvHandler.ErrorOccurred +=
                                (sender, args) => OnErrorOccurred(new EventArgs<Exception>(args.Value));

                            var stream = client.GetStream();
                            _sendHandler.Start(stream);
                            _rcvHandler.Start(stream);

                            OnConnectionEstablished(EventArgs.Empty);

                            System.Diagnostics.Debug.WriteLine("wait for session to execute");
                            wait.WaitOne();
                            _sendHandler.Stop();
                            _rcvHandler.Stop();
                            System.Diagnostics.Debug.WriteLine("session finished");
                            Thread.Sleep(1000);
                        }
                        else
                        {
                            registered = false;
                            ReportError(secondRegResult);
                            Thread.Sleep(3000);
                        }
                       
                    }
                    else
                    {
                        registered = false;
                        ReportError(regResult);
                        Thread.Sleep(3000);
                    }

                }
                catch (Exception ex)
                {
                    _logger.ErrorException("连接数据提交服务器出现错误", ex);
                    OnErrorOccurred(new EventArgs<Exception>(ex));
                    Thread.Sleep(3000);
                }
            }
        }

        private static bool RegistSucceed(RegistrationResult regResult)
        {
            return regResult.Error == null && regResult.Reply.Succeed;
        }

        private void UnRegister(TcpClient client)
        {
            TvPacket packet = TvPacketFactory.CreateUnregisterPacket();
            TlvWriter.Write(client.GetStream(), packet);
        }

        public void Start()
        {
            StartConnectThread(_remoteEp);
        }

        public void Stop()
        {
            if (_cts != null)
            {
                _cts.Cancel();
            }
        }

        public void SendData(CarInfo carInfo)
        {
            _carInfosToBeSend.Add(carInfo);

        }
    }
}
